package org.springblade.common.redisLock;

import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.core.RedisOperations;
import org.springframework.data.redis.core.SessionCallback;
import org.springframework.data.redis.core.StringRedisTemplate;

import java.util.UUID;
import java.util.concurrent.TimeUnit;

public class RedisDisLockManager implements DisLockManager {
	private static final String LOCK_PREFIX = "LOCK_";
	private StringRedisTemplate redisOperations;

	public RedisDisLockManager(StringRedisTemplate redisOperations) {
		this.redisOperations = redisOperations;
	}

	public DisLock tryLock(String name, long timeoutMs, int retryTimes, long retryIntervalMs) {
		String lockId = UUID.randomUUID().toString();
		int execTimes = 0;
		boolean locked = false;

		try {
			while(execTimes < retryTimes) {
				locked = this.tryGetLock(name, lockId, timeoutMs);
				if (locked) {
					break;
				}

				Thread.sleep(retryIntervalMs);
				++execTimes;
			}
		} catch (InterruptedException var11) {
			locked = false;
		}

		if (!locked) {
			throw new DisLockFailException();
		} else {
			return new DisLock(() -> {
				this.releaseLock(name, lockId);
			});
		}
	}

	private boolean tryGetLock(String name, String lockId, long timeoutMs) {
		String lockKey = this.getLockKey(name);
		boolean result = this.redisOperations.opsForValue().setIfAbsent(lockKey, lockId);
		if (result) {
			this.redisOperations.expire(lockKey, timeoutMs, TimeUnit.MILLISECONDS);
		}

		return result;
	}

	private void releaseLock(String name, String lockId) {
		assert lockId != null;

		final String lockKey = this.getLockKey(name);
		this.redisOperations.execute(new SessionCallback<Object>() {
			public Object execute(RedisOperations operations) throws DataAccessException {
				RedisDisLockManager.this.redisOperations.watch(lockKey);
				String existlockId = (String)RedisDisLockManager.this.redisOperations.opsForValue().get(lockKey);
				if (existlockId != null && lockId.equals(existlockId)) {
					RedisDisLockManager.this.redisOperations.multi();
					RedisDisLockManager.this.redisOperations.delete(lockKey);
					return RedisDisLockManager.this.redisOperations.exec();
				} else {
					return null;
				}
			}
		});
	}

	private String getLockKey(String name) {
		assert name != null;

		return "LOCK_" + name;
	}
}
